package com.hulman.oms.service;

import com.hulman.oms.bean.*;
import com.hulman.oms.dao.ProcDefDao;
import com.hulman.oms.dao.ProcInstDao;
import com.hulman.oms.dao.ProcStateDao;
import com.hulman.oms.exception.IoTException;
import com.hulman.oms.util.SubjectUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @Author: maxwellens
 */
@Service
public class ProcInstService
{
    @Autowired
    private ProcInstDao procInstDao;
    @Autowired
    private ProcDefDao procDefDao;
    @Autowired
    private ProcStateDao procStateDao;
    @Autowired
    private TaskService taskService;

    /**
     * 开启流程实例
     *
     * @param key   流程键值
     * @param state 初始状态
     */
    public void startProcess(String key, Integer state)
    {
        ProcDef procDef = procDefDao.findProcDefByKey(key);
        if (procDef != null)
        {
            ProcInst procInst = new ProcInst();
            procInst.setProcDefId(procDef.getId());
            procInst.setKey(procDef.getKey());
            procInst.setName(procDef.getName());
            procInst.setState(state);
            procInst.setStartTime(new Date());
            procInstDao.insertProcInst(procInst);
        } else
        {
            throw new IoTException("不存在键值为" + key + "的流程");
        }
    }

    /**
     * 开启流程实例（默认第一个状态）
     *
     * @param key 流程键值
     */
    public ProcInst startProcess(String key)
    {
        ProcDef procDef = procDefDao.findProcDefByKey(key);
        if (procDef != null)
        {
            ProcInst procInst = new ProcInst();
            procInst.setProcDefId(procDef.getId());
            procInst.setKey(procDef.getKey());
            procInst.setName(procDef.getName());
            ProcState procState = procStateDao.findNextProcState(procDef.getId(), 0);
            if (procState != null)
            {
                procInst.setState(procState.getState());
                procInst.setStateName(procState.getName());
            }
            procInst.setStartTime(new Date());
            procInstDao.insertProcInst(procInst);
            return procInst;
        } else
        {
            throw new IoTException("不存在键值为" + key + "的流程");
        }
    }

    /**
     * 流程进入下一状态
     *
     * @param procInstId
     * @return
     */
    public ProcInst next(Integer procInstId)
    {
        ProcInst procInst = procInstDao.findProcInstById(procInstId);
        if (procInst != null)
        {
            ProcState procState = procStateDao.findNextProcState(procInst.getProcDefId(), procInst.getState());
            if (procState != null)
            {
                procInst.setState(procState.getState());
                procInst.setStateName(procState.getName());
            } else
            {
                procInst.setState(ProcState.STATE_DOCUMENTED);
                procInst.setStateName(ProcState.DOCUMENTED);
                procInst.setEndTime(new Date());
            }
            procInstDao.updateProcInst(procInst);
            return procInst;
        } else
        {
            throw new IoTException("不存在流程实例ID:" + procInstId);
        }
    }

    public ProcInst pre(Integer procInstId)
    {
        ProcInst procInst = procInstDao.findProcInstById(procInstId);
        if (procInst != null)
        {
            ProcState procState = procStateDao.findPreProcState(procInst.getProcDefId(), procInst.getState());
            if (procState != null)
            {
                procInst.setState(procState.getState());
                procInst.setStateName(procState.getName());
            } else
            {
                procInst.setState(ProcState.STATE_DOCUMENTED);
                procInst.setStateName(ProcState.DOCUMENTED);
                procInst.setEndTime(new Date());
            }
            procInstDao.updateProcInst(procInst);
            return procInst;
        } else
        {
            throw new IoTException("不存在流程实例ID:" + procInstId);
        }
    }

    public ProcInst to(Integer procInstId,Integer state)
    {
        ProcInst procInst = procInstDao.findProcInstById(procInstId);
        if (procInst != null)
        {
            ProcState procState = procStateDao.findProcStateByState(procInst.getProcDefId(), state);
            if (procState != null)
            {
                procInst.setState(procState.getState());
                procInst.setStateName(procState.getName());
            } else
            {
                procInst.setState(ProcState.STATE_DOCUMENTED);
                procInst.setStateName(ProcState.DOCUMENTED);
                procInst.setEndTime(new Date());
            }
            procInstDao.updateProcInst(procInst);
            return procInst;
        } else
        {
            throw new IoTException("不存在流程实例ID:" + procInstId);
        }
    }

    /**
     * 强制终止流程实例
     *
     * @param procInstId
     */
    public void terminate(Integer procInstId)
    {
        ProcInst procInst = procInstDao.findProcInstById(procInstId);
        if (procInst != null)
        {
            procInst.setState(ProcState.STATE_TERMINATED);
            procInst.setEndTime(new Date());
            procInstDao.updateProcInst(procInst);
            //结束未完成的任务
            List<Task> onGoingTasks = taskService.findOnGoingTasksByProcInstId(procInst.getId());
            for (Task task : onGoingTasks)
            {
                taskService.uncompleteTask(task.getId());
            }
        }
    }

    public List<ProcInst> findProcInsts(Map<String, Object> map)
    {
        List<ProcInst> procInsts = procInstDao.findProcInsts(map);
        for (ProcInst procInst : procInsts)
        {
            if (procInst.getState() > 0)
            {
                String stateName = procStateDao.findProcStateNameByState(procInst.getProcDefId(), procInst.getState());
                procInst.setStateName(stateName);
            } else
            {
                procInst.setStateName(ProcState.DOCUMENTED);
            }
        }
        return procInsts;
    }

    public Result findProcInstsResult(Map<String, Object> map)
    {
        Integer page = (Integer) map.get("page");
        Integer limit = (Integer) map.get("limit");
        if (page != null && limit != null)
        {
            map.put("start", (page - 1) * limit);
            map.put("length", limit);
        }
        int count = procInstDao.findProcInstsCount(map);
        List<ProcInst> data = findProcInsts(map);
        return new Result(data, count);
    }

    public ProcInst findProcInstById(Integer id)
    {
        return procInstDao.findProcInstById(id);
    }

    public void deleteProcInstById(Integer id)
    {
        procInstDao.deleteProcInstById(id);
    }

}
